Skip to content

feat: [GH-75] Modification-timestamp marker foo[t1~] for added-at-or-after binding#78

Open
javierlores wants to merge 2 commits into
feature/GH-74from
feature/GH-75
Open

feat: [GH-75] Modification-timestamp marker foo[t1~] for added-at-or-after binding#78
javierlores wants to merge 2 commits into
feature/GH-74from
feature/GH-75

Conversation

@javierlores

@javierlores javierlores commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Closes #75.

Stacked on #77 (GH-74). This PR is based on feature/GH-74, so its diff shows only the GH-75 commits. Merge #77 first; GitHub then auto-retargets this PR's base to develop. The two are functionally independent — they just edit the same bracket-parsing chokepoint.

What

Adds an addition-relative key-bracket binding on flat leaf keys: foo[t1~] binds the key to the values whose add occurred in the half-open window [t1, now) — start-inclusive at t1, end-exclusive at the present moment.

locked[1718380800000000~] = null      -- lock values ADDED at or after t
find locked[1700000000~] = "pod-A"    -- ...added at or after t, equal to pod-A

This is grammar / AST / parse only. Server-side evaluation is a separate concourse change (cinchapi/concourse#790).

Distinct from the existing forms

Form Binds
foo[t] the value present at the instant t, regardless of add time
foo[t1...t2] (#74) values present during [t1, t2), regardless of add time
foo[t1~] (this PR) values whose add event falls in [t1, now)

A value added before t1 is excluded by foo[t1~] even if it is still present — that's what powers the motivating stale-lock case: a record whose only lock value was added before the cutoff binds to nothing under locked[<cutoff>~] and is therefore claimable.

How

  • Grammar: accept a standalone ~ (SEARCH_MATCH) as a word token inside KeyBracketParameter only, so a detached marker after a quoted/NL timestamp is captured. ~'s meaning outside brackets is unchanged.
  • Parsing.applyKeyBracket (the chokepoint Temporal range key-bracket binding: foo[t1...t2], foo[...t2], foo[t1...] (grammar/AST/parse only) #74 introduced): trailing ~ModificationKeySymbol; leading ~t (added-before) → rejected as not-yet-supported; a range combined with a marker (t1...t2~) → rejected.
  • ModificationKeySymbol (new, sibling to TemporalKeySymbol): rejects navigation and already-parameterized keys; isParameterized() == true, so writes and audit/chronicle/diff reject it for free via the existing requireNotParameterized gate. Canonical toString()key[<micros>~] (the Keys.parse cross-repo contract).

Validation

  • foo[~t] (added-before) → rejected. It's semantically equivalent to an existence-before range; deferred to that sibling work per the ticket.
  • foo[t1...t2~] (range + marker) → rejected (a marker binds a single instant).
  • foo[t1~][t2] (double bracket) → rejected.

Tests

  • ModificationKeySymbolTest (new): constructor guards, accessor, equals/hashCode, inequality vs TemporalKeySymbol and TemporalRangeKeySymbol with the same key+ts, toString(), parameter hooks.
  • BracketTimestampMatrixTest (M-series): numeric marker, quoted/NL marker (exercises the detached-~ path), keyword equivalence, and the before-marker / range-with-marker / double-bracket rejections, plus round-trip.
  • BracketTimestampCommandTest: accepted on select/get/find; rejected on add and audit.

All pass locally (incl. GrammarTest/JavaCCParserTest/CompilerJavaCCTest regression), parser regenerates cleanly with no new warnings.

Scope / follow-ups

Part of the connector data-sync locking initiative (cinchapi-server). Sibling grammar work: #74.

…r-after binding

Add an addition-relative key-bracket binding on flat leaf keys: foo[t1~]
binds the key to the values whose ADD occurred in the half-open window
[t1, now) -- start-inclusive at t1, end-exclusive at the present moment.

This is distinct from the existing forms: foo[t] binds the value present
AT the instant regardless of add time, and foo[t1...t2] (GH-74) binds
values present DURING an interval regardless of add time. foo[t1~] keys
strictly off the add event, so a value added before t1 is excluded even
if still present. Motivating use: stale-lock detection
(locked[<now-minus-timeout>~] = null).

Grammar/AST/parse only; server-side evaluation is a separate concourse
change. The canonical toString() renders key[<micros>~], a re-parseable
cross-repo contract.

- Accept a standalone '~' (SEARCH_MATCH) inside KeyBracketParameter so a
  detached marker after a quoted/natural-language timestamp is captured;
  '~' outside brackets is unaffected.
- Detect the marker in the centralized Parsing.applyKeyBracket (added by
  GH-74): trailing '~' -> ModificationKeySymbol; reject the leading
  added-before form key[~t] (not yet supported); reject a range combined
  with a marker (key[t1...t2~]).
- Add ModificationKeySymbol (sibling to TemporalKeySymbol): rejects
  navigation and already-parameterized keys, isParameterized() == true so
  writes and audit/chronicle/diff reject it for free.
- Tests: node unit tests, matrix (numeric, quoted/NL, keyword
  equivalence, before-marker / range-with-marker / double-bracket
  rejection, round-trip), command accept (select/get/find) and reject
  (add/audit).
- Update CCL_REFERENCE.md section 8.4.

Markers on navigation stops and scope prefixes remain out of scope
(leaf-only first cut, matching GH-74). Part of the connector data-sync
locking initiative; concourse evaluation is cinchapi/concourse#790.
…cket

Address code-review findings on the modification-marker work:

- Grammar: the SEARCH_MATCH token also lexes the 'search_match'/'contains'
  keyword spellings, which were silently admitted into KeyBracketParameter
  content and only failed later at timestamp parsing. Guard the action to
  admit only the '~' marker and reject the keyword forms with a clear
  message at parse time. Parser regenerated.
- Parsing.applyKeyBracket: use the already-trimmed content uniformly across
  the marker, single-instant, and range branches (was mixing content and
  content.trim()). Behavior unchanged.
- Tests: testM7 confirms a '~' inside a quoted timestamp is not read as a
  marker; testM8 confirms a search keyword in a bracket is rejected.
@javierlores javierlores changed the base branch from develop to feature/GH-74 June 24, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Key-bracket grammar: modification-timestamp marker foo[t1~] for added-at-or-after binding

1 participant